package com.googlecode.javatools.classparser.impl;

import java.io.DataInput;
import java.io.IOException;

import com.googlecode.javatools.classparser.ConstantParser;
import com.googlecode.javatools.classparser.types.ConstantEntry;
import com.googlecode.javatools.classparser.types.impl.ConstantEntryDefault;

public class ConstantParserImpl implements ConstantParser
{
	private static final int CONSTANT_UTF8 = 1;
	private static final int CONSTANT_INTEGER = 3;
	private static final int CONSTANT_FLOAT = 4;
	private static final int CONSTANT_LONG = 5;
	private static final int CONSTANT_DOUBLE = 6;
	private static final int CONSTANT_CLASS = 7;
	private static final int CONSTANT_STRING = 8;
	private static final int CONSTANT_FIELD = 9;
	private static final int CONSTANT_METHOD = 10;
	private static final int CONSTANT_INTERFACEMETHOD = 11;
	private static final int CONSTANT_NAMEANDTYPE = 12;

	public boolean isEightByteConstantThatUsesTwoPoolEntries(final ConstantEntry constant)
	{
		return constant.getTag() == CONSTANT_DOUBLE || constant.getTag() == CONSTANT_LONG;
	}

	public ConstantEntry parseNextConstant(final DataInput dataInputStream) throws Exception
	{
		ConstantEntry result;

		final byte tag = dataInputStream.readByte();

		switch (tag)
		{
		case (CONSTANT_CLASS):
			result = constantWithNameInfo(dataInputStream, tag);
			break;
		case (CONSTANT_STRING):
			result = constantWithNameInfo(dataInputStream, tag);
			break;
		case (CONSTANT_FIELD):
			result = constantWithNameAndTypeInfo(dataInputStream, tag);
			break;
		case (CONSTANT_METHOD):
			result = constantWithNameAndTypeInfo(dataInputStream, tag);
			break;
		case (CONSTANT_INTERFACEMETHOD):
			result = constantWithNameAndTypeInfo(dataInputStream, tag);
			break;
		case (CONSTANT_NAMEANDTYPE):
			result = constantWithNameAndTypeInfo(dataInputStream, tag);
			break;
		case (CONSTANT_INTEGER):
			result = new ConstantEntryDefault(tag, Integer.valueOf(dataInputStream.readInt()));
			break;
		case (CONSTANT_FLOAT):
			result = new ConstantEntryDefault(tag, Float.valueOf(dataInputStream.readFloat()));
			break;
		case (CONSTANT_LONG):
			result = new ConstantEntryDefault(tag, Long.valueOf(dataInputStream.readLong()));
			break;
		case (CONSTANT_DOUBLE):
			result = new ConstantEntryDefault(tag, Double.valueOf(dataInputStream.readDouble()));
			break;
		case (CONSTANT_UTF8):
			result = new ConstantEntryDefault(tag, dataInputStream.readUTF());
			break;
		default:
			throw new IllegalStateException("Unknown constant: " + tag);
		}

		return result;
	}

	private ConstantEntry constantWithNameInfo(final DataInput dataInputStream, final byte tag) throws IOException
	{
		ConstantEntry result;
		result = new ConstantEntryDefault(tag, dataInputStream.readUnsignedShort());
		return result;
	}

	private ConstantEntry constantWithNameAndTypeInfo(final DataInput dataInputStream, final byte tag)
			throws IOException
	{
		ConstantEntry result;
		result = new ConstantEntryDefault(tag, dataInputStream.readUnsignedShort(), dataInputStream.readUnsignedShort());
		return result;
	}

}
